home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / formats / iff / newiff.lzh / NewIFF / NewIFF.lzh / newiff / apps / PlaySMUS / PlaySMUS.c < prev   
C/C++ Source or Header  |  1992-05-18  |  29KB  |  1,029 lines

  1.  
  2. /** PlaySMUS.c ************************************************************** 
  3.  * 
  4.  * Read and play a SMUS file
  5.  * 
  6.  * requires linkage with several IFF modules - see Makefile
  7.  ****************************************************************************/ 
  8.  
  9. #include "iffp/8svxapp.h"
  10. #include "iffp/smusapp.h"
  11.  
  12. #include <exec/execbase.h>
  13. #include <graphics/gfxbase.h>
  14. #include <clib/alib_protos.h>
  15.  
  16. #ifdef LATTICE
  17. int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */
  18. int chkabort(void) { return(0); }  /* really */
  19. #endif
  20.  
  21. /**********    debug macros     ***********/
  22. #define MYDEBUG  1
  23. void kprintf(UBYTE *fmt,...);
  24. void dprintf(UBYTE *fmt,...);
  25. #define DEBTIME 0
  26. #define bug printf
  27. #if MYDEBUG
  28. #define D(x) (x); if(DEBTIME>0) Delay(DEBTIME);
  29. #else
  30. #define D(x) ;
  31. #endif /* MYDEBUG */
  32. /********** end of debug macros **********/
  33.  
  34. /* prototypes for our functions */
  35. void cleanup(void);
  36. void bye(UBYTE *s,int error);
  37. void DUnpack(BYTE source[], LONG n, BYTE dest[]);
  38. BYTE D1Unpack(BYTE source[], LONG n, BYTE dest[], BYTE x);
  39. LONG LoadSample(struct EightSVXInfo *esvx, UBYTE *filename);
  40. void UnloadSample(struct EightSVXInfo *esvx);
  41. LONG LoadSBody(struct EightSVXInfo *esvx);
  42. void UnloadSBody(struct EightSVXInfo *esvx);
  43. LONG ShowSample(struct EightSVXInfo *esvx);
  44.  
  45. LONG LoadSMUS(struct SMUSInfo *smus, UBYTE *filename);
  46. void UnloadSMUS(struct SMUSInfo *esvx);
  47.  
  48. LONG OpenAudio(void);
  49. void CloseAudio(void);
  50. LONG PlaySample(struct EightSVXInfo *esvx,
  51.         LONG octave, LONG note, UWORD volume, ULONG delay);
  52.  
  53. #define MINARGS 2
  54. char *vers = "$VER: PlaySMUS 37.9";
  55. char *Copyright = "PlaySMUS v37.9 (Freely Redistributable)";
  56. char *usage = "Usage: PlaySMUS SMUSname [instrument path]";
  57.  
  58. char *inspath = "Instruments:";
  59.  
  60. /* globals */
  61. struct Library *IFFParseBase   = NULL;
  62. struct Library *GfxBase = NULL;
  63.  
  64. BOOL   FromWb;
  65.  
  66. /* SMUS Property chunks to be grabbed
  67.  */
  68. LONG    smusprops[] = {
  69.         ID_SMUS, ID_SHDR,
  70.         ID_SMUS, ID_NAME,
  71.         ID_SMUS, ID_AUTH,
  72.         ID_SMUS, ID_Copyright,
  73.         TAG_DONE
  74.         };
  75.  
  76. /* SMUS Collection chunks (more than one in file) to be gathered */
  77. LONG    smuscollects[] = {
  78.         ID_SMUS, ID_ANNO,
  79.         TAG_DONE
  80.         };
  81.  
  82. /* SMUS Chunks to stop on */
  83. LONG    smusstops[] = {
  84.         ID_SMUS, ID_TRAK,
  85.         ID_SMUS, ID_INS1,
  86.         TAG_DONE
  87.         };
  88.  
  89.  
  90. /* 8SVX Property chunks to be grabbed
  91.  */
  92. LONG    esvxprops[] = {
  93.         ID_8SVX, ID_VHDR,
  94.         ID_8SVX, ID_NAME,
  95.         ID_8SVX, ID_ATAK,
  96.         ID_8SVX, ID_RLSE,
  97.         ID_8SVX, ID_AUTH,
  98.         ID_8SVX, ID_Copyright,
  99.         TAG_DONE
  100.         };
  101.  
  102. /* 8SVX Collection chunks (more than one in file) to be gathered */
  103. LONG    esvxcollects[] = {
  104.         ID_8SVX, ID_ANNO,
  105.         TAG_DONE
  106.         };
  107.  
  108. /* 8SVX Chunk to stop on */
  109. LONG    esvxstops[] = {
  110.         ID_8SVX, ID_BODY,
  111.         TAG_DONE
  112.         };
  113.  
  114.  
  115. UBYTE nomem[]  = "Not enough memory\n";
  116. UBYTE noiffh[] = "Can't alloc iff\n";
  117.  
  118.  
  119.  
  120. /* For our allocated SMUSInfo */
  121. struct SMUSInfo *smus = NULL;
  122.  
  123. /* For allocated 8SVX Infos */
  124. struct EightSVXInfo  *esvxs[MAXINS] = { 0 };
  125. struct EightSVXInfo  *esvx = NULL;
  126. ULONG  ei = 0, ti = 0, tcnt = 0, ecnt = 0;
  127.  
  128.  
  129. /* 
  130.  * MAIN 
  131.  */
  132. void main(int argc, char **argv)
  133.    {
  134.    UBYTE *smusname=NULL, *esvxname=NULL;
  135.    ULONG oct;
  136.    LONG error=0L;
  137.  
  138.    FromWb = argc ? FALSE : TRUE;
  139.  
  140.    if((argc<MINARGS)||(argv[argc-1][0]=='?'))
  141.     {
  142.     printf("%s\n%s\n",Copyright,usage);
  143.         bye("",RETURN_OK);
  144.     }
  145.  
  146.    smusname = argv[1];
  147.    if(argc > 2)  inspath = argv[2];
  148.  
  149. /* Open Libraries */
  150.    if(!(IFFParseBase = OpenLibrary("iffparse.library",0)))
  151.       bye("Can't open iffparse library.\n",RETURN_WARN);
  152.  
  153.  
  154. /* 
  155.  * Alloc one SMUSInfo struct
  156.  */
  157.     if(!(smus = (struct SMUSInfo *)
  158.     AllocMem(sizeof(struct SMUSInfo),MEMF_PUBLIC|MEMF_CLEAR))) 
  159.         bye(nomem,RETURN_FAIL);
  160.  
  161. /*
  162.  * Here we set up our SMUSInfo fields for our application.
  163.  * Above we have defined the propery and collection chunks
  164.  * we are interested in.
  165.  * We want to stop on INS1 or TRAK.
  166.  */
  167.     smus->ParseInfo.propchks    = smusprops;
  168.     smus->ParseInfo.collectchks    = smuscollects;
  169.     smus->ParseInfo.stopchks    = smusstops;
  170. /* 
  171.  * Alloc the IFF handle for the frame
  172.  */
  173.     if(!(smus->ParseInfo.iff = AllocIFF())) bye(noiffh,RETURN_FAIL);
  174.  
  175.  
  176. /* 
  177.  * Alloc one EightSVXInfo struct for defaults
  178.  */
  179.     if(!(esvx = (struct EightSVXInfo *)
  180.     AllocMem(sizeof(struct EightSVXInfo),MEMF_PUBLIC|MEMF_CLEAR))) 
  181.         bye(nomem,RETURN_FAIL);
  182.  
  183. /*
  184.  * Here we set up default EightSVXInfo fields for our
  185.  * application.
  186.  * Above we have defined the propery and collection chunks
  187.  * we are interested in (some required like VHDR).
  188.  * We want to stop on BODY.
  189.  */
  190.     esvx->ParseInfo.propchks    = esvxprops;
  191.     esvx->ParseInfo.collectchks    = esvxcollects;
  192.     esvx->ParseInfo.stopchks    = esvxstops;
  193. /* 
  194.  * Alloc the IFF handle for the frame
  195.  */
  196.     if(!(esvx->ParseInfo.iff = AllocIFF())) bye(noiffh,RETURN_FAIL);
  197.  
  198.     if(error = LoadSMUS(smus, smusname))
  199.     {
  200.     printf("Error %ld: %s\n",error, IFFerr(error));
  201.     goto done;
  202.     }
  203. goto done;
  204.  
  205.     if(!(error = LoadSample(esvx, esvxname)))
  206.     {
  207.     ShowSample(esvx);
  208.  
  209.     if(!(error = OpenAudio()))
  210.         {
  211.         /* If we think this is a sound effect, play it as such (note=-1) */
  212.         if((esvx->Vhdr.ctOctave==1)&&(esvx->Vhdr.samplesPerSec)
  213.         &&(esvx->Vhdr.oneShotHiSamples)&&(!esvx->Vhdr.repeatHiSamples))
  214.         {
  215.         PlaySample(esvx,0,-1,64,0);
  216.         }
  217.         /* Else play it like an instrument */
  218.         else
  219.         {
  220.             for(oct=0; oct < esvx->Vhdr.ctOctave; oct++)
  221.             {
  222.                 PlaySample(esvx,oct,0,64,50);
  223.                 PlaySample(esvx,oct,4,64,50);
  224.                 PlaySample(esvx,oct,7,64,50);
  225.             }
  226.         }
  227.         CloseAudio();
  228.         }
  229.         else printf("error opening audio device\n");
  230.     }
  231.     else
  232.         printf("%s\n",IFFerr(error));
  233.  
  234. done:
  235.     D(bug("done\n"));
  236.     cleanup();
  237.     exit(RETURN_OK);
  238.     }
  239.  
  240.  
  241. void bye(UBYTE *s,int error)
  242.    {
  243.    if((*s)&&(!FromWb)) printf("%s\n",s);
  244.    cleanup();
  245.    exit(error);
  246.    }
  247.  
  248.  
  249. void cleanup()
  250.    {
  251.    int k;
  252.  
  253.    D(bug("cleanup:\n"));
  254.  
  255.    if(esvx)        
  256.     {
  257. /*
  258.     DD(bug("About to UnloadSample\n"));
  259.     UnloadSample(esvx);
  260. */
  261.     if(esvx->ParseInfo.iff)     FreeIFF(esvx->ParseInfo.iff);
  262.         FreeMem(esvx,sizeof(struct EightSVXInfo));
  263.     esvx = NULL;
  264.     }
  265.  
  266.    for(k = 0; (k <MAXINS) && (esvxs[k]) ; k++)
  267.     {
  268.     if(esvxs[k])
  269.         {
  270.         D(bug("cleanup: about to UnloadSample %ld\n",k));
  271.         UnloadSample(esvxs[k]);
  272.         D(bug("cleanup: about to FreeIFF %ld\n",k));
  273.         if(esvxs[k]->ParseInfo.iff)     FreeIFF(esvxs[k]->ParseInfo.iff);
  274.             FreeMem(esvxs[k],sizeof(struct EightSVXInfo));
  275.         esvxs[k] = NULL;
  276.         }
  277.     }
  278.  
  279.    if(smus)
  280.     {
  281.     DD(bug("About to UnloadSMUS\n"));
  282.     UnloadSMUS(smus);
  283.     if(smus->ParseInfo.iff)     FreeIFF(smus->ParseInfo.iff);
  284.     FreeMem(smus,sizeof(struct SMUSInfo));
  285.     smus = NULL;
  286.     }
  287.  
  288.    if(IFFParseBase)      CloseLibrary(IFFParseBase);
  289.    }
  290.  
  291.  
  292. /** ShowSample() **********************************************
  293.  * 
  294.  * Show sample information after calling LoadSample()
  295.  * 
  296.  *************************************************************************/
  297. LONG ShowSample(struct EightSVXInfo *esvx)
  298.     {
  299.     LONG error = 0L;
  300.     BYTE *buf;
  301.     Voice8Header *vhdr;
  302.  
  303.     if(!esvx)            return(CLIENT_ERROR);
  304.     if(!(buf = esvx->sample))    return(CLIENT_ERROR);
  305.  
  306.     /* LoadSample copied VHDR and NAME (if any) to our esvx frame */
  307.     vhdr = &esvx->Vhdr;
  308.     if(esvx->name[0]) printf("\nNAME: %s",esvx->name);
  309.  
  310.     printf("\n\nVHDR Info:");
  311.     printf("\noneShotHiSamples=%ld", vhdr->oneShotHiSamples); 
  312.     printf("\nrepeatHiSamples=%ld", vhdr->repeatHiSamples); 
  313.     printf("\nsamplesPerHiCycle=%ld", vhdr->samplesPerHiCycle); 
  314.     printf("\nsamplesPerSec=%ld", vhdr->samplesPerSec); 
  315.     printf("\nctOctave=%ld", vhdr->ctOctave); 
  316.     printf("\nsCompression=%ld", vhdr->sCompression); 
  317.     printf("\nvolume=0x%lx", vhdr->volume); 
  318.     printf("\nData = %3ld %3ld %3ld %3ld %3ld %3ld %3ld %3ld",  
  319.            buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]); 
  320.     printf("\n       %3ld %3ld %3ld %3ld %3ld %3ld %3ld %3ld ...\n",  
  321.            buf[8+0],buf[8+1],buf[8+2],buf[8+3],buf[8+4],buf[8+5],
  322.            buf[8+6],buf[8+ 7]); 
  323.  
  324.     return(error);
  325.     } 
  326.  
  327.  
  328. /* OpenAudio
  329.  *
  330.  * Opens audio device for one audio channel, 2 IO requests
  331.  * Returns 0 for success
  332.  *
  333.  * Based on code by Dan Baker
  334.  */
  335.  
  336. UBYTE           whichannel[] = { 1,2,4,8 };
  337.  
  338. /* periods for scale starting at   65.40Hz (C) with 128 samples per cycle
  339.  *                            or  130.81Hz (C) with  64 samples per cycle
  340.  *                            or  261.63Hz (C) with  32 samples per cycle
  341.  *                            or  523.25Hz (C) with  16 samples per cycle
  342.  *                            or 1046.50Hz (C) with   8 samples per cycle
  343.  *                            or 2093.00Hz (C) with   4 samples per cycle
  344.  */
  345.  
  346. UWORD   per_ntsc[12]= { 428, 404, 380, 360,
  347.             340, 320, 302, 286,
  348.             270, 254, 240, 226 };
  349.  
  350. /* periods adjusted for system clock frequency */
  351. UWORD   per[12];
  352.  
  353. /* Note - these values 3579545 NTSC, 3546895 PAL */
  354. #define NTSC_CLOCK 3579545L
  355. #define PAL_CLOCK  3546895L
  356.  
  357. #define AIOCNT 4
  358. struct     IOAudio *aio[AIOCNT] = {NULL};       /* Ptrs to IO blocks for commands  */
  359.  
  360. struct     MsgPort *port;       /* Pointer to a port so the device can reply */
  361. BOOL    devopened;
  362. ULONG    clock = NTSC_CLOCK;     /* Will check for PAL and change if necessary */
  363.  
  364.  
  365. LONG OpenAudio()
  366. {
  367. extern    struct ExecBase *SysBase;
  368. LONG     error=0L;
  369. ULONG   period;
  370. int    k;
  371.  
  372. if(devopened)    return(-1);
  373.  
  374. /*-------------------------------------------------------------------------*/
  375. /* Ask the system if we are PAL or NTSC and set clock constant accordingly */
  376. /*-------------------------------------------------------------------------*/
  377. if(GfxBase=OpenLibrary("graphics.library",0L))
  378.     {
  379.     if(((struct GfxBase *)GfxBase)->DisplayFlags & PAL)
  380.         clock = PAL_CLOCK;
  381.     else
  382.         clock = NTSC_CLOCK;
  383.     CloseLibrary((struct Library *) GfxBase);
  384.     }
  385.  
  386. printf("OpenAudio: For period calculations, clock=%ld\n", clock);
  387.  
  388. /* calculate period values for one octave based on system clock */
  389. for(k=0; k<12; k++)
  390.     {
  391.     period = ((per_ntsc[k] * clock) + (NTSC_CLOCK >> 1)) / NTSC_CLOCK;
  392.     per[k] = period;
  393.     D(bug("per[%ld]=%ld ",k,per[k]));
  394.     }
  395. D(bug("\n"));
  396.  
  397. /*-------------------------------------------------------------------*/
  398. /* Create a reply port so the audio device can reply to our commands */
  399. /*-------------------------------------------------------------------*/
  400. if(!(port=CreatePort(0,0)))
  401.     { error = 1; goto bailout; }
  402.  
  403. /*--------------------------------------------------------------------------*/
  404. /*  Create audio I/O blocks so we can send commands to the audio device     */
  405. /*--------------------------------------------------------------------------*/
  406. for(k=0; k<AIOCNT; k++)
  407.     {
  408.     if(!(aio[k]=(struct IOAudio *)CreateExtIO(port,sizeof(struct IOAudio))))
  409.     { error = k+2; goto bailout; }
  410.     }
  411.  
  412. /*----------------------------------------------------------------------*/
  413. /* Set up the audio I/O block for channel allocation:                   */
  414. /* ioa_Request.io_Message.mn_ReplyPort is the address of a reply port.  */
  415. /* ioa_Request.io_Message.mn_Node.ln_Pri sets the precedence (priority) */
  416. /*   of our use of the audio device. Any tasks asking to use the audio  */
  417. /*   device that have a higher precedence will steal the channel from us.*/
  418. /* ioa_Request.io_Command is the command field for IO.                  */
  419. /* ioa_Request.io_Flags is used for the IO flags.                       */
  420. /* ioa_AllocKey will be filled in by the audio device if the allocation */
  421. /*   succeeds. We must use the key it gives for all other commands sent.*/
  422. /* ioa_Data is a pointer to the array listing the channels we want.     */
  423. /* ioa_Length tells how long our list of channels is.                   */
  424. /*----------------------------------------------------------------------*/
  425. aio[0]->ioa_Request.io_Command               = ADCMD_ALLOCATE;
  426. aio[0]->ioa_Request.io_Flags                 = ADIOF_NOWAIT;
  427. aio[0]->ioa_AllocKey                         = 0;
  428. aio[0]->ioa_Data                             = whichannel;
  429. aio[0]->ioa_Length                           = sizeof(whichannel);
  430.  
  431. /*-----------------------------------------------*/
  432. /* Open the audio device and allocate a channel  */
  433. /*-----------------------------------------------*/
  434. if(!(OpenDevice("audio.device",0L, (struct IORequest *) aio[0] ,0L)))
  435.     devopened = TRUE;
  436. else { error = 5; goto bailout; }
  437.  
  438. /* Clone the flags, channel allocation, etc. into other IOAudio requests */
  439. for(k=1; k<AIOCNT; k++)    *aio[k] = *aio[0];
  440.  
  441. bailout:
  442. if(error)    
  443.     {
  444.     printf("OpenAudio errored out at step %ld\n",error);
  445.     CloseAudio();
  446.     }
  447. return(error);
  448. }
  449.  
  450.  
  451. /* CloseAudio
  452.  *
  453.  * Close audio device as opened by OpenAudio, null out pointers
  454.  */
  455. void CloseAudio()
  456. {
  457. int k;
  458.  
  459. D(bug("Closing audio device...\n"));
  460.  
  461. /* Note - we know we have no outstanding audio requests */
  462. if(devopened)
  463.     {
  464.     CloseDevice((struct IORequest *) aio[0]);
  465.     devopened = FALSE;
  466.     }
  467.  
  468. for(k=0; k<AIOCNT; k++)
  469.     {
  470.     if(aio[k])     DeleteExtIO(aio[k]), aio[k] = NULL;
  471.     }
  472.  
  473. if(port)       DeletePort(port),  port = NULL;
  474. }
  475.  
  476.  
  477. /** PlaySample() **********************************************
  478.  * 
  479.  * Play a note in octave for delay/50ths of a second 
  480.  * OR Play a sound effect (set octave and note to 0, -1)
  481.  *
  482.  * Requires successful OpenAudio() called previously
  483.  *
  484.  * When playing notes:
  485.  * Expects note values between 0 (C) and 11 (B#)
  486.  * Uses largest octave sample in 8SVX as octave 0, next smallest
  487.  *   as octave 1, etc.
  488.  *
  489.  * Notes - this simple example routine does not do ATAK and RLSE)
  490.  *       - use of Delay for timing is simplistic, synchronous, and does
  491.  *        not take into account that the oneshot itself may be
  492.  *        longer than the delay.
  493.  *         Use timer.device for more accurate asynchronous delays
  494.  *
  495.  *************************************************************************/
  496. /* Max playable sample in one IO request is 128K */
  497. #define MAXSAMPLE 131072
  498.  
  499. LONG    PlaySample(struct EightSVXInfo *esvx,
  500.             LONG octave, LONG note, UWORD volume, ULONG delay)
  501. {
  502. /* pointers to outstanding requests */
  503. struct        IOAudio    *aout0=NULL, *aout1=NULL;    
  504. ULONG        period;
  505. LONG        osize, rsize;
  506. BYTE        *oneshot, *repeat;
  507.  
  508. if(!devopened)    return(-1);
  509.  
  510. if(note > 11) note=0;
  511.  
  512. if( note == -1 ) period = clock / esvx->Vhdr.samplesPerSec;
  513. else          period = per[note]; /* table set up by OpenAudio */
  514.  
  515. if(octave > esvx->Vhdr.ctOctave) octave = 0;
  516. if(volume > 64)    volume = 64;
  517.  
  518. oneshot = esvx->osamps[octave];
  519. osize   = esvx->osizes[octave];
  520. repeat  = esvx->rsamps[octave];
  521. rsize   = esvx->rsizes[octave];
  522.  
  523. D(bug("oneshot $%lx size %ld, repeat $%lx size %ld\n",
  524.     oneshot, osize, repeat, rsize));
  525.  
  526. /*------------------------------------------------------------*/
  527. /* Set up audio I/O blocks to play a sample using CMD_WRITE.  */
  528. /* Set up one request for the oneshot and one for repeat      */
  529. /* (all ready for simple case, but we may not need both)      */
  530. /* The io_Flags are set to ADIOF_PERVOL so we can set the     */
  531. /*    period (speed) and volume with the our sample;          */
  532. /* ioa_Data points to the sample; ioa_Length gives the length */
  533. /* ioa_Cycles tells how many times to repeat the sample       */
  534. /* If you want to play the sample at a given sampling rate,   */
  535. /* set ioa_Period = clock/(given sampling rate)               */
  536. /*------------------------------------------------------------*/
  537. aio[0]->ioa_Request.io_Command             =CMD_WRITE;
  538. aio[0]->ioa_Request.io_Flags               =ADIOF_PERVOL;
  539. aio[0]->ioa_Data                           =oneshot;
  540. aio[0]->ioa_Length                         =osize;
  541. aio[0]->ioa_Period                         =period;
  542. aio[0]->ioa_Volume                         =volume;
  543. aio[0]->ioa_Cycles                         =1;
  544.  
  545. aio[2]->ioa_Request.io_Command             =CMD_WRITE;
  546. aio[2]->ioa_Request.io_Flags               =ADIOF_PERVOL;
  547. aio[2]->ioa_Data                           =repeat;
  548. aio[2]->ioa_Length                         =rsize;
  549. aio[2]->ioa_Period                         =period;
  550. aio[2]->ioa_Volume                         =volume;
  551. aio[2]->ioa_Cycles                         =0;    /* repeat until stopped */
  552.  
  553. /*---------------------------------------------------*/
  554. /* Send the command to start a sound using BeginIO() */
  555. /* Go to sleep and wait for the sound to finish with */
  556. /* WaitIO() to wait and get the get the ReplyMsg     */
  557. /*---------------------------------------------------*/
  558. printf("Starting tone O len %ld for %0ld cyc, R len %ld for %0ld cyc, per=%ld...",
  559.         osize, aio[0]->ioa_Cycles, rsize, aio[1]->ioa_Cycles, period);
  560.  
  561. if(osize)
  562.     {
  563.     /* Simple case for oneshot sample <= 128K (ie. most samples) */
  564.     if(osize <= MAXSAMPLE)    BeginIO((struct IORequest *)(aout0=aio[0]));
  565.      }
  566.  
  567. if(rsize)
  568.     {
  569.     /* Simple case for oneshot sample <= 128K (ie. most samples) */
  570.     if(rsize <= MAXSAMPLE)    BeginIO((struct IORequest *)(aout1=aio[2]));
  571.     }
  572.  
  573. if(delay)    Delay(delay);    /* crude timing for notes */
  574.  
  575. /* Wait for any requests we still have out */
  576. if(aout0) WaitIO(aout0);
  577.  
  578. if(aout1)
  579.    {
  580.    if(note >= 0) AbortIO(aout1);    /* if a note, stop it now */
  581.    WaitIO(aout1);
  582.    }
  583.  
  584. printf("Done\n");
  585. }
  586.  
  587.  
  588. /** LoadSample() **********************************************************
  589.  * 
  590.  * Read 8SVX, given an initialized EightSVXInfo with not-in-use IFFHandle,
  591.  *   and filename.  Leaves the IFFHandle open so you can FindProp()
  592.  *   additional chunks or copychunks().  You must UnloadSample()
  593.  *   when done.  UnloadSample will closeifile if the file is still
  594.  *   open.
  595.  *
  596.  * Fills in esvx->Vhdr and Name, and allocates/loads esvx->sample,
  597.  *   setting esvx->samplebytes to size for deallocation.
  598.  *
  599.  * Returns 0 for success of an IFFERR (libraries/iffparse.h)
  600.  *************************************************************************/
  601. LONG LoadSample(struct EightSVXInfo *esvx, UBYTE *filename)
  602.     {
  603.     struct IFFHandle *iff;
  604.     struct StoredProperty *sp;
  605.     Voice8Header *vhdr;
  606.     BYTE *oneshot, *repeat;
  607.     ULONG osize, rsize, spcyc;
  608.     int oct;
  609.     LONG error = 0L;
  610.  
  611.     D(bug("LoadSample: looking for %s\n",filename));
  612.     
  613.     if(!esvx)                return(CLIENT_ERROR);
  614.     if(!(iff=esvx->ParseInfo.iff))    return(CLIENT_ERROR);
  615.  
  616.     if(!(error = openifile((struct ParseInfo *)esvx, filename, IFFF_READ)))
  617.     {
  618.     printf("Reading '%s'...\n",filename);
  619.     error = parseifile((struct ParseInfo *)esvx,
  620.             ID_FORM, ID_8SVX,
  621.             esvx->ParseInfo.propchks,
  622.             esvx->ParseInfo.collectchks,
  623.             esvx->ParseInfo.stopchks);
  624.  
  625.     D(bug("LoadSample: after parseifile - error = %ld\n",error));
  626.  
  627.     if((!error)||(error == IFFERR_EOC)||(error == IFFERR_EOF))
  628.         {
  629.         if(contextis(iff,ID_8SVX,ID_FORM))
  630.         {
  631.         D(bug("LoadSample: context is 8SVX\n"));
  632.         if(!(sp = FindProp(iff,ID_8SVX,ID_VHDR)))
  633.             {
  634.             message("No 8SVX.VHDR!");
  635.             error = IFFERR_SYNTAX;
  636.             }
  637.         else
  638.             {
  639.             D(bug("LoadSample: Have VHDR\n"));
  640.             /* copy Voice8Header into frame */
  641.             vhdr = (Voice8Header *)(sp->sp_Data);
  642.             *(&esvx->Vhdr) = *vhdr;
  643.             /* copy name if any */
  644.             esvx->name[0]='\0';
  645.             if(sp = FindProp(iff,ID_8SVX,ID_NAME))
  646.             {
  647.             strncpy(esvx->name,sp->sp_Data,sp->sp_Size);
  648.             esvx->name[MIN(sp->sp_Size,79)] = '\0';
  649.             }
  650.                 error = LoadSBody(esvx);
  651.             D(bug("LoadSample: After LoadSBody - error = %ld\n",error));
  652.             if(!error)
  653.             {
  654.             osize   = esvx->Vhdr.oneShotHiSamples;
  655.             rsize   = esvx->Vhdr.repeatHiSamples;
  656.             spcyc    = esvx->Vhdr.samplesPerHiCycle;
  657.             if(!spcyc) spcyc = esvx->Vhdr.repeatHiSamples;
  658.             if(!spcyc) spcyc = 8;
  659.  
  660.             oneshot = esvx->sample;
  661.  
  662.             for(oct = esvx->Vhdr.ctOctave-1; oct >= 0;
  663.                  oct--, oneshot+=(osize+rsize),
  664.                     osize <<= 1, rsize <<=1, spcyc <<=1)
  665.                     {
  666.                     repeat  = oneshot + osize;
  667.                 esvx->osizes[oct] = osize;
  668.                 if(osize) esvx->osamps[oct] = oneshot;
  669.                 else      esvx->osamps[oct] = 0;
  670.                 esvx->rsizes[oct] = rsize;
  671.                 if(rsize) esvx->rsamps[oct] = repeat;
  672.                 else      esvx->rsamps[oct] = 0;
  673.                 esvx->spcycs[oct] = spcyc;
  674.  
  675.              D(bug("oneshot $%lx size %ld, repeat $%lx size %ld\n",
  676.                 oneshot, osize, repeat, rsize));
  677.  
  678.                 }
  679.                 }
  680.             }
  681.         }
  682.         else
  683.         {
  684.         message("Not an 8SVX\n");
  685.         error = NOFILE;
  686.         }
  687.         }
  688.     }
  689.  
  690.     if(error)
  691.     {
  692.     closeifile((struct ParseInfo *)esvx);
  693.     UnloadSample(esvx);
  694.     }
  695.     return(error);
  696.     }
  697.  
  698.  
  699. /** UnloadSample() *******************************************************
  700.  * 
  701.  * Frees and closes everything opened/alloc'd by LoadSample()
  702.  *
  703.  *************************************************************************/
  704. void UnloadSample(struct EightSVXInfo *esvx)
  705.     {
  706.     if(esvx)
  707.     {
  708.     D(bug("UnloadSample: About to UnloadSBody\n"));
  709.     UnloadSBody(esvx);
  710.     D(bug("UnloadSample: About to closeifile\n"));
  711.     closeifile((struct ParseInfo *)esvx);
  712.     }
  713.     }
  714.  
  715.  
  716. /** LoadSBody() ***********************************************************
  717.  * 
  718.  * Read a 8SVX Sample BODY into RAM.  
  719.  * 
  720.  *************************************************************************/
  721. LONG LoadSBody(struct EightSVXInfo *esvx)
  722.     {
  723.     struct IFFHandle *iff;
  724.     LONG sbytes, rlen, error = 0L; 
  725.     ULONG memtype;
  726.     Voice8Header *vhdr = &esvx->Vhdr;
  727.     BYTE *t;
  728.  
  729.     D(bug("LoadSBody:\n"));
  730.  
  731.     if(!(iff=esvx->ParseInfo.iff))    return(CLIENT_ERROR);
  732.     if(!esvx)                return(CLIENT_ERROR);
  733.  
  734.     if(!(currentchunkis(iff,ID_8SVX,ID_BODY)))
  735.     {
  736.     message("LoadSBody: not at BODY!");
  737.     return(IFFERR_READ);
  738.     }
  739.  
  740.     sbytes  = ChunkMoreBytes(CurrentChunk(iff)); 
  741.  
  742.     /* if we have to decompress, let's just load it into public mem */
  743.     memtype = vhdr->sCompression ? MEMF_PUBLIC : MEMF_CHIP;
  744.  
  745.     D(bug("LoadSBody: samplebytes=%ld, compression=%ld\n",
  746.             sbytes,vhdr->sCompression));
  747.     
  748.     if(!(esvx->sample = (BYTE *)AllocMem(sbytes, memtype))) 
  749.     {
  750.         error = CLIENT_ERROR; 
  751.     }
  752.     else 
  753.     {
  754.     D(bug("LoadSBody: have load buffer\n"));
  755.     esvx->samplebytes = sbytes; 
  756.         if(rlen=ReadChunkBytes(iff,esvx->sample,sbytes) != sbytes)
  757.         error = IFFERR_READ;
  758.  
  759.     if(error)
  760.         {
  761.         D(bug("LoadSBody: ReadChunkBytes error = %ld, read %ld bytes\n",
  762.             error));
  763.         UnloadSample(esvx);
  764.         }
  765.     else if (vhdr->sCompression) /* Decompress, if needed. */
  766.         { 
  767.             if(t = (BYTE *)AllocMem(sbytes<<1, MEMF_CHIP)) 
  768.         {
  769.         D(bug("LoadSBody: have decompression buffer\n"));
  770.                 DUnpack(esvx->sample, sbytes, t); 
  771.                 FreeMem(esvx->sample, sbytes); 
  772.                 esvx->sample = t;
  773.                 esvx->samplebytes = sbytes << 1;
  774.         }
  775.         else
  776.         {
  777.         UnloadSample(esvx);
  778.         error = IFFERR_NOMEM;
  779.         }
  780.         } 
  781.     }
  782.     return(error);
  783.     } 
  784.  
  785.  
  786. /** UnloadSBody() ********************************************************
  787.  * 
  788.  * Deallocates esvx->smaple  
  789.  * 
  790.  *************************************************************************/
  791. void UnloadSBody(struct EightSVXInfo *esvx)
  792.     {
  793.     if(esvx)
  794.     {
  795.     D(bug("UnloadSBody:\n"));
  796.     if(esvx->sample)
  797.         {
  798.         D(bug("About to free SBody\n"));
  799.         FreeMem(esvx->sample,esvx->samplebytes);
  800.         esvx->sample = NULL;
  801.         }
  802.     esvx->samplebytes = NULL;
  803.     }
  804.     }
  805.  
  806.  
  807. /* DUnpack.c --- Fibonacci Delta decompression by Steve Hayes */
  808.  
  809. /* Fibonacci delta encoding for sound data */
  810. BYTE codeToDelta[16] = {-34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21};
  811.  
  812. /* Unpack Fibonacci-delta encoded data from n byte source
  813.  * buffer into 2*n byte dest buffer, given initial data
  814.  * value x.  It returns the lats data value x so you can
  815.  * call it several times to incrementally decompress the data.
  816.  */
  817.  
  818. BYTE D1Unpack(BYTE source[], LONG n, BYTE dest[], BYTE x)
  819.    {
  820.    BYTE d;
  821.    LONG i, lim;
  822.  
  823.    lim = n << 1;
  824.    for (i=0; i < lim; ++i)
  825.       {
  826.       /* Decode a data nibble, high nibble then low nibble */
  827.       d = source[i >> 1];    /* get a pair of nibbles */
  828.       if (i & 1)             /* select low or high nibble */
  829.          d &= 0xf;           /* mask to get the low nibble */
  830.       else
  831.          d >>= 4;            /* shift to get the high nibble */
  832.       x += codeToDelta[d];   /* add in the decoded delta */
  833.       dest[i] = x;           /* store a 1 byte sample */
  834.       }
  835.    return(x);
  836.    }
  837.  
  838. /* Unpack Fibonacci-delta encoded data from n byte
  839.  * source buffer into 2*(n-2) byte dest buffer.
  840.  * Source buffer has a pad byte, an 8-bit initial
  841.  * value, followed by n-2 bytes comprising 2*(n-2)
  842.  * 4-bit encoded samples.
  843.  */
  844.  
  845. void DUnpack(source, n, dest)
  846. BYTE source[], dest[];
  847. LONG n;
  848.    {
  849.    D1Unpack(source+2, n-2, dest, source[1]);
  850.    }
  851.  
  852.  
  853. /** LoadSMUS() **********************************************************
  854.  * 
  855.  * Read SMUS, given an initialized SMUSInfo with not-in-use IFFHandle,
  856.  *   and filename.  Leaves the IFFHandle open so you can FindProp()
  857.  *   additional chunks or copychunks().  You must UnloadSMUS()
  858.  *   when done.  UnloadSMUS will closeifile if the file is still
  859.  *   open.
  860.  *
  861.  * Fills in smus->Shdr and Name, and sets up smus traks[],
  862.  *   array setting smus->nevents[] to number of events in each.
  863.  *
  864.  * Returns 0 for success of an IFFERR (libraries/iffparse.h)
  865.  *************************************************************************/
  866. LONG LoadSMUS(struct SMUSInfo *smus, UBYTE *filename)
  867.     {
  868.     struct ContextNode *cn;
  869.     struct IFFHandle *iff;
  870.     struct StoredProperty *sp;
  871.     SScoreHeader *shdr;
  872.     LONG error = 0L, rlen, inslock, oldlock;
  873.     UBYTE insname[80];
  874.  
  875.     D(bug("LoadSMUS:\n"));
  876.     
  877.     if(!smus)                return(CLIENT_ERROR);
  878.     if(!(iff=smus->ParseInfo.iff))    return(CLIENT_ERROR);
  879.  
  880.     if(!(error = openifile((struct ParseInfo *)smus, filename, IFFF_READ)))
  881.     {
  882.     printf("Reading '%s'...\n",filename);
  883.     error = parseifile((struct ParseInfo *)smus,
  884.             ID_FORM, ID_SMUS,
  885.             smus->ParseInfo.propchks,
  886.             smus->ParseInfo.collectchks,
  887.             smus->ParseInfo.stopchks);
  888.  
  889.     D(bug("LoadSMUS: after parseifile - error:%ld, %s\n",error,IFFerr(error)));
  890.  
  891.     if((!error)||(error == IFFERR_EOC)||(error == IFFERR_EOF))
  892.         {
  893.         if(contextis(iff,ID_SMUS,ID_FORM))
  894.         {  /* Is a SMUS */
  895.         D(bug("LoadSMUS: context is SMUS\n"));
  896.         if(!(sp = FindProp(iff,ID_SMUS,ID_SHDR)))
  897.             {
  898.             message("No SMUS.SHDR!");
  899.             error = IFFERR_SYNTAX;
  900.             }
  901.         else
  902.             {
  903.             D(bug("LoadSMUS: Have SHDR\n"));
  904.             /* copy SScoreHeader into frame */
  905.             shdr = (SScoreHeader *)(sp->sp_Data);
  906.             *(&smus->Shdr) = *shdr;
  907.             /* copy name if any */
  908.             smus->name[0]='\0';
  909.             if(sp = FindProp(iff,ID_SMUS,ID_NAME))
  910.             {
  911.             strncpy(smus->name,sp->sp_Data,sp->sp_Size);
  912.             smus->name[MIN(sp->sp_Size,79)] = '\0';
  913.             }
  914.                 while(1)
  915.                     {
  916.                     /* We only asked to stop at SMUS INS1 and TRAK chunks
  917.                      * If no error we've hit a stop chunk
  918.                      */
  919.                     cn = CurrentChunk(iff);
  920.  
  921.                     if((cn)&&(cn->cn_Type == ID_SMUS)&&
  922.                     (cn->cn_ID == ID_INS1))
  923.                             {
  924.                             printf("INS1 chunk found\n");
  925.                 if(esvxs[ei] = (struct EightSVXInfo *)
  926.                 AllocMem(sizeof(struct EightSVXInfo),
  927.                         MEMF_PUBLIC|MEMF_CLEAR))
  928.                 {
  929.                 /* Clone but needs new handle */
  930.                 *(esvxs[ei]) = *esvx;
  931.                     if(!(esvxs[ei]->ParseInfo.iff =
  932.                     AllocIFF())) bye(noiffh,RETURN_FAIL); 
  933.  
  934.                 /* Get name of sound */
  935.                         rlen=ReadChunkBytes(iff,&smus->insflags[ei],4);
  936.                         rlen=ReadChunkBytes(iff,insname,
  937.                       MIN(cn->cn_Size - 4, 79));
  938.  
  939.                 if(rlen != (cn->cn_Size-4)) error = IFFERR_READ;
  940.                 else insname[rlen] = '\0';
  941.  
  942.                 D(bug("Instrument name is %s\n",insname));
  943.                 if(!error)
  944.                     {
  945.                     if(inslock = Lock(inspath,ACCESS_READ))
  946.                     {
  947.                     oldlock = CurrentDir(inslock);
  948.                     error = LoadSample(esvxs[ei],insname);
  949.                     CurrentDir(oldlock);
  950.                     UnLock(inslock);
  951.                     }
  952.                 if (error) printf("Can't load instrument %s\n",
  953.                             insname);
  954.                     }
  955.                 ei++;
  956.                 ecnt++;
  957.                 }
  958.                 }
  959.             else if((cn)&&(cn->cn_Type == ID_SMUS)&&
  960.                     (cn->cn_ID == ID_TRAK))
  961.                 {
  962.                 printf("TRAK chunk found, size=%ld\n",
  963.                     cn->cn_Size);
  964.  
  965.                 if( smus->traks[ti]  = (struct SEvent *)
  966.                 AllocMem(cn->cn_Size, MEMF_PUBLIC))
  967.                 {
  968.                     smus->tbytes[ti] = cn->cn_Size;
  969.                     if((rlen=ReadChunkBytes(iff,smus->traks[ti],
  970.                       cn->cn_Size)) != cn->cn_Size)
  971.                         error = IFFERR_READ;
  972.                     printf("   Loaded at $%lx\n",smus->traks[ti]);
  973.                 ti++;
  974.                 tcnt++;
  975.                 }
  976.                 else error = IFFERR_NOMEM;
  977.                 }
  978.             if(error) break;
  979.                     else error = ParseIFF(iff,IFFPARSE_SCAN);
  980.                     if(error == IFFERR_EOC) continue;       /* enter next context */
  981.                     else if(error) break;
  982.             }
  983.             }
  984.         }
  985.         else
  986.         {
  987.         message("Not a SMUS\n");
  988.         error = NOFILE;
  989.         }
  990.         }
  991.     }
  992.  
  993.     if(error == IFFERR_EOC) error = 0;
  994.  
  995.     D(bug("LoadSMUS: error = %ld\n",error));
  996.  
  997.     if(error)
  998.     {
  999.     closeifile((struct ParseInfo *)smus);
  1000.     UnloadSMUS(smus);
  1001.     }
  1002.  
  1003.     return(error);
  1004.     }
  1005.  
  1006.  
  1007. /** UnloadSMUS() *******************************************************
  1008.  * 
  1009.  * Frees and closes everything opened/alloc'd by LoadSMUS()
  1010.  *
  1011.  *************************************************************************/
  1012. void UnloadSMUS(struct SMUSInfo *smus)
  1013.     {
  1014.     int ti;
  1015.  
  1016.     D(bug("In UnloadSMUS\n"));
  1017.     if(smus)
  1018.     {
  1019.     for(ti=0; (ti < MAXTRACKS) && (smus->traks[ti]); ti++)
  1020.         {
  1021.         printf("Freeing %ld at $%lx\n",smus->traks[ti],smus->tbytes[ti]);
  1022.         FreeMem(smus->traks[ti],smus->tbytes[ti]);
  1023.         smus->traks[ti] = NULL;
  1024.         }
  1025.     closeifile((struct ParseInfo *)smus);
  1026.     }
  1027.     }
  1028.  
  1029.